import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { EMPTY, from, Observable, Subject, throwError } from 'rxjs';
import { catchError, switchMap } from 'rxjs/operators';
import { AuthService } from '../service/auth.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  private refreshSubject: Subject<any> = new Subject<any>();
  constructor(private readonly authService: AuthService) {}
  public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (req.url.endsWith('/login') || req.url.endsWith('/refreshToken')) {
      return next.handle(req);
    } else {
      return this.handleRequestWithToken(req, next);
    }
  }

  private handleRequestWithToken(req: HttpRequest<any>, next: HttpHandler) {
    const reqWithToken = this.updateHeader(req);

    return next.handle(reqWithToken).pipe(
      catchError((error, caught) => {
        if (error instanceof HttpErrorResponse) {
          if (this.checkAccessTokenExpiryErr(error)) {
            return this.checkIfTokenExpired()
              .pipe(
                switchMap(async () => {
                  await this.sleep(100);
                  const reqWithToken1 = this.updateHeader(req);
                  return next.handle(reqWithToken1).toPromise();
                }),
                catchError((e) => {
                  if (e.status === 403) {
                    this.authService.logout();
                    return EMPTY;
                  }
                  return throwError(e);
                })
              )
              .toPromise();
          } else {
            return throwError(error);
          }
        }
        return caught;
      })
    );
  }
  private sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
  }
  private updateHeader(req) {
    const authToken = this.authService.getAccessToken();

    req = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${authToken}`),
    });
    return req;
  }
  private checkIfTokenExpired() {
    this.refreshSubject.subscribe((res) => {
      this.refreshSubject = new Subject<any>();
    });
    if (this.refreshSubject.observers.length === 1) {
      this.authService.refreshTokens().subscribe(this.refreshSubject);
    }
    return this.refreshSubject;
  }

  private checkAccessTokenExpiryErr(error: HttpErrorResponse): boolean {
    return error.status && error.status === 401;
  }
}
